package com.tomtop.core.dorequest;

import java.lang.reflect.Type;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestParam;

import com.tomtop.core._enum.CustomStatus;
import com.tomtop.core.cache.CacheService;
import com.tomtop.core.cache.impl.DefaultCacheServiceImpl;
import com.tomtop.core.exception.CustomException;
import com.tomtop.core.logging.RunTimeLog;
import com.tomtop.core.utils.JSONUtils;
import com.tomtop.core.utils.ListUtils;
import com.tomtop.core.utils.MapUtils;
import com.tomtop.core.utils.StringUtils;

@Aspect
@Order(7)
@Component
public class HttpRequestAop {
    private final static Logger LOGGER = LoggerFactory.getLogger(HttpRequestAop.class);

    @Autowired HttpRequestHandle httpRequestHandle;
    @Autowired(required=false) CacheService cacheService=new DefaultCacheServiceImpl();
    /**
     * 需要解析的参数注解
     */
	private final static  String[] ats=new String[]{RequestParam.class.getTypeName(),RequestHeader.class.getTypeName(),RequestBody.class.getName(),CookieValue.class.getTypeName(),  PathVariable.class.getTypeName()};
    
    @Pointcut("@annotation(com.tomtop.core.dorequest.DoHttpRequest)")
    public void httpRequest() {
    }
    
    @Around(value="httpRequest()",argNames="proceedingJoinPoint")
    public Object recordHttp(ProceedingJoinPoint point) throws Throwable {
    	LOGGER.debug("HttpRequest代理请求");
    	
        Object[] args = point.getArgs();
        DoHttpRequestData requestData = AnnotationAnalysis.initMethodParameter(point);
        if((!requestData.isCache())||requestData.getMethod()!=HttpMethod.GET)
        	return this.doHttpRequest(point, requestData, args);
       return  cacheService.goCache(requestData.getMethodKey(), args,requestData.getCachetimeout() , ()->this.doHttpRequest(point, requestData, args));
    }
    
    private Object doHttpRequest(ProceedingJoinPoint point, DoHttpRequestData requestData, Object[] args) throws Throwable  {
    
    	HttpVariable variable=initHttpParameter(requestData.getVariable(),args);
    	if(StringUtils.isNotEmpty(requestData.getVersion()))
    		variable.getParam().put("version", requestData.getVersion());
    	long start=System.currentTimeMillis();
    	HttpContext httpContext = httpRequestHandle.handle(requestData,variable);
		long runtime=System.currentTimeMillis()-start;
		RunTimeLog.log(runtime, "方法{} 发请求耗时:{}毫秒  响应的数据:{}",requestData.getMethodKey(),runtime, httpContext.getResponse());
    	try {
    		if(requestData.isManuallyprocessed()) 
    			return point.proceed(); 
        	return this.autoReturn(httpContext, requestData.getReturnType());
		} finally {
			httpContext.close();
		}
	}
    
    private Object autoReturn(HttpContext httpResponse,Type returnType){
		try {
			if(httpResponse.getResponse()==null){
				return null;
			}
			String relust = httpResponse.getBody();
	    	if(returnType.getTypeName().equals(String.class.getName()))
				return relust;
			return JSONUtils.fromObject(relust, returnType);
		} catch (Exception e) {
			LOGGER.error("发请求，结果集转化为json失败");
    		throw new CustomException(CustomStatus.DO_REQUEST_DESERIALIZATION_FAIL ,e);
		}
    }
    
    private HttpVariable initHttpParameter(Map<String,Map<String,List<Integer>>> param,Object[] args) {
    	List<Map<String,Object>> list=new LinkedList<>();
    	for (String atkey : ats) {
        	Map<String, Object> rmap=new LinkedHashMap<>();
        	list.add(rmap);
        	Map<String, List<Integer>> map = param.get(atkey);
        	if(MapUtils.isEmpty(map))
        		continue;
        	map.forEach((name,l)->{
        		if(ListUtils.isEmpty(l))
        			return;
        		if(l.size()==1){
        			rmap.put(name, args[l.get(0)]);
        		} else{
        			List<Object> vals=new LinkedList<>();
        			l.forEach(k->vals.add(args[k]));
        			rmap.put(name, vals);
        		}
        	});
		}
    	
    	HttpVariable variable=new HttpVariable();
    	variable.setParam(list.get(0));
    	variable.setHeader(list.get(1));
    	variable.setBody(list.get(2));
    	variable.setCookie(list.get(3));
    	variable.setPath(list.get(4));
    	return variable;
	}
}
